#pragma once

#include <iostream>
#include <string>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>

#include "Log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"
#include "Daemon.hpp"

#define SIZE 1024

uint16_t defaultport = 8081;
const std::string defaultip = "0.0.0.0";
static const int backlog = 10;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    LISTEN_ERR
};

class TcpServer;
// 多线程版本通信
class ThreadData
{
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer* tsvr;
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t)
        :sockfd(fd),clientip(ip),clientport(port),tsvr(t)
    {}
};

class TcpServer
{
private:
    int listen_sock_;
    std::string ip_;
    uint16_t port_;
public:
    TcpServer(uint16_t port = defaultport, const std::string &ip = defaultip)
        : port_(port), ip_(ip),listen_sock_(-1)
    {
    }
    ~TcpServer()
    {
        close(listen_sock_);
    }

private:
    // 1.创建套接字
    void Socket()
    {
        listen_sock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_sock_ < 0)
        {
            lg(Fatal, "scoket create error, errno: %d, error_string: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        lg(Info, "scoket create success, listen_sock_: %d", listen_sock_);
        // 设置套接字描述符的属性
        // 防止偶发性的服务器无法进行立即重启
        int opt = 1;
        setsockopt(listen_sock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));
    }
    // 2.绑定套接字
    void Bind()
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = INADDR_ANY;
        socklen_t len = sizeof(local);

        if (bind(listen_sock_, (const sockaddr *)&local, len) < 0)
        {
            lg(Fatal, "socket bind error,  errno: %d, error_string: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "socket bind success...");
    }
    // 3.监听套接字
    void Listen()
    {
        // TCP 是面向连接的，服务器一般是比较“被动的”，在没有客户端访问的时候就只能干等，不能退出，服务器需要一直处于一种，一直在等待连接到来的状态
        if (listen(listen_sock_, backlog) < 0)
        {
            lg(Fatal, "socket listen error, errno: %d, error_string: %s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        lg(Info, "socket listen success...");
    }
    // 通信 -- version1 -- 单进程版本
    void Service(int sockfd, const uint16_t &client_port, const std::string &client_ip)
    {
        char buffer[SIZE];
        while (true)
        {
            bzero(buffer, sizeof(buffer));
            // 读取 客户端发送来的信息
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if(n > 0){
                buffer[n] = 0;
                std::cout << "client say@ " << buffer << std::endl;
                
                // 返回 信息给 客户端
                std::string echo_string  = "tcpserver echo# ";
                echo_string += buffer;
                int m = write(sockfd, echo_string.c_str(), echo_string.size());
                if(m < 0){
                    lg(Warning, "write error...");
                }
                // std::cout << "write success..." << std::endl;
            }
            else if(n == 0){    // 全部读取完毕
                lg(Info, "[%s-%d]-> Client Quit..., sockfd: %d", client_ip.c_str(), client_port, sockfd);
                break;
            }
            else{   // 读取错误
                lg(Warning, "read error...");
                break;
            }
        }
    }
    // 通信 -- version2 -- 多进程版
    void Service2(int listensock_, int sockfd, const uint16_t &client_port, const std::string &client_ip)
    {
        pid_t id = fork();
        if(id == 0){    // child
            close(listensock_);
            if(fork() > 0) exit(0); // 子进程再创建子进程，然后让子进程直接退出，由孙子进程执行后续代码
            Service(sockfd, client_port, client_ip);
            close(sockfd);
            exit(0);
        }
        // parent
        close(sockfd);
        int rid = waitpid(id, nullptr, 0);
    }
    // 通信 -- version3 -- 多线程版
    static void *Routine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData*>(args);
        td->tsvr->Service(td->sockfd, td->clientport, td->clientip);
        delete td;
        return nullptr;
    }
public:
    // 初始化 TCP 服务器
    void InitTcpServer()
    {
        Socket();
        Bind();
        Listen();
    }
    // 开始运行 TCP 服务器
    void Start()
    {
        Daemon();
        // signal(SIGPIPE, SIG_IGN);   // 防止出现一些自己关闭的文件写的操作
        lg(Info, "tcpserver is running...");
        ThreadPool<Task>::GetInstance()->Start();
        while (true)
        {
            // 4.获取新链接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listen_sock_, (sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                lg(Warning, "accept error,  errno: %d, error_string: %s", errno, strerror(errno));
                continue;
            }
            uint16_t client_port = ntohs(client.sin_port);
            char client_ip[32];
            inet_ntop(AF_INET, &(client.sin_addr), client_ip, sizeof(client_ip));

            // 5.根据新链接 进行通信
            // version1 -- 单进程版本
            // Service(sockfd, client_port, client_ip);
            // close(sockfd);

            // version2 -- 多进程版
            // Service2(listen_sock_, sockfd, client_port, client_ip);
            
            // version3 -- 多线程版
            // ThreadData *td = new ThreadData(sockfd, client_ip, client_port, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            // version4 -- 线程池版
            Task t(sockfd, client_ip, client_port);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
};
